use clap::Parser;
use std::{fmt, path::Path, str::FromStr};

// Copy: 数据量比较小的, 在栈上的数据才应该实现Copy trait
#[derive(Debug, Clone, Copy)]
pub enum OutputFormat {
    // 指定输出类型
    Json,
    Yaml,
}

#[derive(Debug, Parser)]
pub struct CsvOpts {
    // short表示该参数支持短命令的形式, 即: 去变量名的第一个字符, -i
    // long表示该参数支持长命令的形式, 即: 完整的变量名, --input
    // value_parser可以对参数进行校验, 可以使用clap模块内置的相关检验函数, 也可以自定义函数
    #[arg(short, long, value_parser = verify_input_file)]
    pub input: String,

    // default_value表示该参数的默认值, 如果没有提供该参数, 则使用默认值
    #[arg(short, long)]
    pub output: Option<String>,

    // 指定输出的格式
    #[arg(long, value_parser = parse_format, default_value = "json")]
    pub format: OutputFormat,

    #[arg(short, long, default_value_t = ',')]
    pub delimiter: char,

    // 由于默认的命令行都会占用-h这个短命令参数用于help输出
    // 因此这里的header short短命令会与help的短命令冲突
    // 要么去掉short, 只保留长参数--header
    // 要么header更换名字
    // #[arg(short, long, default_value_t = true)]
    #[arg(long, default_value_t = true)]
    pub header: bool,
}

fn verify_input_file(filename: &str) -> Result<String, String> {
    // 检查文件是否存在
    if Path::new(filename).exists() {
        Ok(filename.to_string())
    } else {
        Err(format!("Input file {} does not exist", filename))
    }
}

// 关于value_parser返回值Result中的第二个参数
// 只要返回的类型最终能够被转换为String的话都可以
// 因为当发生错误时最终这个参数需要被转换为String在终端中打印出来
fn parse_format(format: &str) -> Result<OutputFormat, anyhow::Error> {
    format.parse()
}

// Rust中From和Into是一对trait, 当实现了From trait的时就自动实现了Into trait
// impl From<OutputFormat> for &'static str {
//     fn from(format: OutputFormat) -> Self {
//         match format {
//             OutputFormat::Json => "json",
//             OutputFormat::Yaml => "yaml",
//         }
//     }
// }

// 为OutputFormat枚举实现FromStr trait, 用于实现&str类型的parse方法
impl FromStr for OutputFormat {
    type Err = anyhow::Error;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        // 校验当前所支持的输出类型
        match s {
            "json" => Ok(OutputFormat::Json),
            "yaml" => Ok(OutputFormat::Yaml),
            _ => Err(anyhow::anyhow!("Unsupported format: {}", s)),
        }
    }
}

impl fmt::Display for OutputFormat {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            OutputFormat::Json => write!(f, "json"),
            OutputFormat::Yaml => write!(f, "yaml"),
        }
    }
}